// Copyright (c) MatrixFramework. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Users;
using PhotoGalleryService.Application.Contracts.Photos;
using Volo.Abp.Linq;

namespace PhotoGalleryService.Photos;

/// <summary>
/// 照片应用服务实现
/// </summary>
[Authorize]
public class PhotoAppService : CrudAppService<
    Photo,
    PhotoDto,
    Guid,
    PagedAndSortedResultRequestDto,
    CreateUpdatePhotoDto>, IPhotoAppService
{
    private readonly IRepository<Photo, Guid> _photoRepository;
    private readonly ICurrentUser _currentUser;

    public PhotoAppService(
        IRepository<Photo, Guid> repository,
        ICurrentUser currentUser) : base(repository)
    {
        _photoRepository = repository;
        _currentUser = currentUser;
    }

    /// <summary>
    /// 上传照片
    /// </summary>
    /// <param name="input">上传输入参数</param>
    /// <returns>照片信息</returns>
    public async Task<PhotoDto> UploadAsync(PhotoUploadInput input)
    {
        // 计算文件哈希
        var fileHash = ComputeSHA256Hash(input.FileContent);

        // 检查是否已存在相同哈希的照片
        var existingPhoto = await _photoRepository.FirstOrDefaultAsync(x => x.FileHash == fileHash);
        if (existingPhoto != null)
        {
            return ObjectMapper.Map<Photo, PhotoDto>(existingPhoto);
        }

        // 创建新照片记录
        var photo = new Photo(
            GuidGenerator.Create(),
            GenerateFileName(),
            input.FileName,
            GenerateFilePath(),
            input.FileContent.Length,
            GetMimeType(input.FileName),
            fileHash
        );

        // 保存文件到磁盘（简化实现）
        await SavePhotoFileAsync(photo, input.FileContent);

        await _photoRepository.InsertAsync(photo);

        return ObjectMapper.Map<Photo, PhotoDto>(photo);
    }

    /// <summary>
    /// 下载照片
    /// </summary>
    /// <param name="id">照片ID</param>
    /// <returns>照片下载信息</returns>
    public async Task<PhotoDownloadDto> DownloadAsync(Guid id)
    {
        var photo = await _photoRepository.GetAsync(id);

        // 读取文件内容
        var fileContent = await ReadPhotoFileAsync(photo.FilePath);

        return new PhotoDownloadDto
        {
            Id = photo.Id,
            FileName = photo.OriginalFileName,
            MimeType = photo.MimeType,
            ContentType = photo.MimeType,
            Content = fileContent,
            FileSize = fileContent.Length
        };
    }

    /// <summary>
    /// 批量上传照片
    /// </summary>
    /// <param name="inputs">上传输入参数列表</param>
    /// <returns>照片信息列表</returns>
    public async Task<List<PhotoDto>> BatchUploadAsync(List<PhotoUploadInput> inputs)
    {
        var results = new List<PhotoDto>();

        foreach (var input in inputs)
        {
            try
            {
                var photoDto = await UploadAsync(input);
                results.Add(photoDto);
            }
            catch (Exception ex)
            {
                // 记录错误但继续处理其他照片
                Logger.LogWarning(ex, "批量上传照片时发生错误：{FileName}", input.FileName);
            }
        }

        return results;
    }

    /// <summary>
    /// 获取照片内容
    /// </summary>
    /// <param name="id">照片ID</param>
    /// <param name="size">图片尺寸</param>
    /// <returns>文件流</returns>
    public async Task<byte[]> GetContentAsync(Guid id, string size = "original")
    {
        var photo = await _photoRepository.GetAsync(id);
        var filePath = size switch
        {
            "thumbnail" when !string.IsNullOrEmpty(photo.ThumbnailPath) => photo.ThumbnailPath,
            "medium" when !string.IsNullOrEmpty(photo.MediumPath) => photo.MediumPath,
            _ => photo.FilePath
        };

        return await ReadPhotoFileAsync(filePath);
    }

    /// <summary>
    /// 根据哈希查找照片
    /// </summary>
    /// <param name="fileHash">文件哈希</param>
    /// <returns>照片信息</returns>
    public async Task<PhotoDto?> FindByHashAsync(string fileHash)
    {
        var photo = await _photoRepository.FirstOrDefaultAsync(x => x.FileHash == fileHash);
        return photo != null ? ObjectMapper.Map<Photo, PhotoDto>(photo) : null;
    }

    /// <summary>
    /// 获取我的照片
    /// </summary>
    /// <param name="input">分页请求</param>
    /// <returns>分页结果</returns>
    public async Task<PagedResultDto<PhotoDto>> GetMyPhotosAsync(PagedAndSortedResultRequestDto input)
    {
        var currentUserId = _currentUser.Id;
        if (currentUserId == null)
        {
            return new PagedResultDto<PhotoDto>(0, new List<PhotoDto>());
        }

        var queryable = await _photoRepository.GetQueryableAsync();
        queryable = queryable.Where(x => x.CreatorId == currentUserId);

        var totalCount = await AsyncExecuter.CountAsync(queryable);
        var items = await AsyncExecuter.ToListAsync(
            queryable.OrderBy(x => x.CreationTime)
                .PageBy(input)
        );

        var dtos = ObjectMapper.Map<List<Photo>, List<PhotoDto>>(items);
        return new PagedResultDto<PhotoDto>(totalCount, dtos);
    }

    /// <summary>
    /// 获取公开照片
    /// </summary>
    /// <param name="input">分页请求</param>
    /// <returns>分页结果</returns>
    public async Task<PagedResultDto<PhotoDto>> GetPublicPhotosAsync(PagedAndSortedResultRequestDto input)
    {
        var queryable = await _photoRepository.GetQueryableAsync();
        queryable = queryable.Where(x => x.IsPublic);

        var totalCount = await AsyncExecuter.CountAsync(queryable);
        var items = await AsyncExecuter.ToListAsync(
            queryable.OrderBy(x => x.CreationTime)
                .PageBy(input)
        );

        var dtos = ObjectMapper.Map<List<Photo>, List<PhotoDto>>(items);
        return new PagedResultDto<PhotoDto>(totalCount, dtos);
    }

    /// <summary>
    /// 搜索照片
    /// </summary>
    /// <param name="input">搜索输入参数</param>
    /// <returns>搜索结果</returns>
    public async Task<PagedResultDto<PhotoDto>> SearchAsync(PhotoSearchInput input)
    {
        var queryable = await _photoRepository.GetQueryableAsync();

        queryable = queryable
            .WhereIf(!input.Keyword.IsNullOrWhiteSpace(),
                x => x.Title.Contains(input.Keyword) ||
                     x.Description.Contains(input.Keyword) ||
                     x.Tags.Contains(input.Keyword))
            .WhereIf(input.StartTime.HasValue, x => x.CreationTime >= input.StartTime)
            .WhereIf(input.EndTime.HasValue, x => x.CreationTime <= input.EndTime)
            .WhereIf(input.IsPublic.HasValue, x => x.IsPublic == input.IsPublic);

        var totalCount = await AsyncExecuter.CountAsync(queryable);
        var items = await AsyncExecuter.ToListAsync(
            queryable.OrderBy(x => x.CreationTime)
                .PageBy(input.SkipCount, input.MaxResultCount)
        );

        var dtos = ObjectMapper.Map<List<Photo>, List<PhotoDto>>(items);

        return new PagedResultDto<PhotoDto>(totalCount, dtos);
    }

    /// <summary>
    /// 计算文件的SHA256哈希
    /// </summary>
    /// <param name="fileBytes">文件字节数组</param>
    /// <returns>十六进制哈希字符串</returns>
    private static string ComputeSHA256Hash(byte[] fileBytes)
    {
        using var sha256 = SHA256.Create();
        var hashBytes = sha256.ComputeHash(fileBytes);
        return Convert.ToHexString(hashBytes).ToLowerInvariant();
    }

    /// <summary>
    /// 生成文件名
    /// </summary>
    /// <returns>文件名</returns>
    private static string GenerateFileName()
    {
        return $"{Guid.NewGuid():N}";
    }

    /// <summary>
    /// 生成文件路径
    /// </summary>
    /// <returns>文件路径</returns>
    private static string GenerateFilePath()
    {
        var date = DateTime.Now.ToString("yyyy/MM/dd");
        return $"uploads/photos/{date}";
    }

    /// <summary>
    /// 根据文件名获取MIME类型
    /// </summary>
    /// <param name="fileName">文件名</param>
    /// <returns>MIME类型</returns>
    private static string GetMimeType(string fileName)
    {
        var extension = Path.GetExtension(fileName).ToLowerInvariant();
        return extension switch
        {
            ".jpg" or ".jpeg" => "image/jpeg",
            ".png" => "image/png",
            ".gif" => "image/gif",
            ".webp" => "image/webp",
            ".bmp" => "image/bmp",
            _ => "application/octet-stream"
        };
    }

    /// <summary>
    /// 保存照片文件到磁盘
    /// </summary>
    /// <param name="photo">照片实体</param>
    /// <param name="fileContent">文件内容</param>
    /// <returns></returns>
    private static async Task SavePhotoFileAsync(Photo photo, byte[] fileContent)
    {
        // 简化实现，实际项目中应该使用专门的文件存储服务
        var directory = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", photo.FilePath);
        Directory.CreateDirectory(directory);

        var filePath = Path.Combine(directory, $"{photo.FileName}{Path.GetExtension(photo.OriginalFileName)}");
        await File.WriteAllBytesAsync(filePath, fileContent);

        // 更新照片的文件路径
        photo.FilePath = Path.Combine(photo.FilePath, $"{photo.FileName}{Path.GetExtension(photo.OriginalFileName)}");
    }

    /// <summary>
    /// 读取照片文件内容
    /// </summary>
    /// <param name="filePath">文件路径</param>
    /// <returns>文件内容</returns>
    private static async Task<byte[]> ReadPhotoFileAsync(string filePath)
    {
        var fullPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", filePath);
        return await File.ReadAllBytesAsync(fullPath);
    }
}